﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Text;

using Weazel.Gezel.Model;

namespace Weazel.Gezel.Conversions.Model
{
  /// <summary>
  /// Only used for inports on ipblocks of type 'queue'.
  /// </summary>
  public class InPortType : Type
  {
    protected InPort inport;
    private Method evaluateMethod;
    private Method initializeMethod;

    private IntegerField valueField;
    protected Field cycleField;

    public InPortType(InPort inport, SoftwareModel softwareModel)
      : base(String.Format("IpBlockInPort_{0}", inport.Name.Replace(".", "_")), softwareModel)
    {
      this.inport = inport;      

      valueField = AddIntegerField("Value", inport.Width, inport.Signed, "0");
      cycleField = AddField("Cycle", "Int32");

      buildEvaluateMethod(inport, softwareModel);
      
      buildInitializeMethod();
    }

    private void buildEvaluateMethod(InPort inport, SoftwareModel softwareModel)
    {
      evaluateMethod = AddMethod("Evaluate");

			if (softwareModel.PortMappings.ContainsInport(inport))
			{
				OutPort outport = softwareModel.PortMappings[inport];
				string outportInstanceName = string.Format("outPort_{0}", outport.Name.Replace(".", "_"));

				// add assert statement
				{
					CodeTypeReferenceExpression simulatorTypeReference = new CodeTypeReferenceExpression("Simulator");
					CodeExpression argument = new CodeBinaryOperatorExpression(
						new CodeFieldReferenceExpression(new CodeFieldReferenceExpression(simulatorTypeReference, outportInstanceName), "Cycle"),
						CodeBinaryOperatorType.IdentityEquality,
						new CodeFieldReferenceExpression(simulatorTypeReference, "CurrentCycle")
					);
					CodeMethodReferenceExpression assertMethod = new CodeMethodReferenceExpression(new CodeTypeReferenceExpression("Debug"), "Assert");
					evaluateMethod.Statements.Add(new CodeMethodInvokeExpression(assertMethod, argument));
				}


				CodeFieldReferenceExpression fieldReference = new CodeFieldReferenceExpression(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Simulator"), outportInstanceName), "Value");
				CodeMethodReferenceExpression valueCopyMethod = new CodeMethodReferenceExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "Value"), "ValueCopy");
				evaluateMethod.Statements.Add(new CodeMethodInvokeExpression(valueCopyMethod, fieldReference));

			}
			else
			{
				System.Diagnostics.Trace.Assert(softwareModel.SignalMappings.ContainsInport(inport));

				Signal signal = softwareModel.SignalMappings[inport];

				SignalType signalType = softwareModel.SignalTypes[signal.Name];

				string signalInstanceName = string.Format("signal_{0}", signal.Name.Replace(".", "_"));

        // trigger evaluation of the signal
        {
          CodeFieldReferenceExpression signalReference =
            new CodeFieldReferenceExpression(
              new CodeTypeReferenceExpression("Simulator"),
              signalInstanceName
            );
          CodeMethodReferenceExpression method = new CodeMethodReferenceExpression(signalReference, "Evaluate");
          evaluateMethod.Statements.Add(new CodeMethodInvokeExpression(method));
        }


				CodeFieldReferenceExpression fieldReference = new CodeFieldReferenceExpression(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Simulator"), signalInstanceName), "Value");
				CodeMethodReferenceExpression valueCopyMethod = new CodeMethodReferenceExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "Value"), "ValueCopy");
				evaluateMethod.Statements.Add(new CodeMethodInvokeExpression(valueCopyMethod, fieldReference));
			}
    }

    private void buildInitializeMethod()
    {
      initializeMethod = AddMethod("Initialize");
      initializeMethod.Statements.Add(
        new CodeAssignStatement(
          new CodeFieldReferenceExpression(
            new CodeThisReferenceExpression(),
            valueField.Name
          ),
          new CodeObjectCreateExpression(
            "Integer",
            new CodePrimitiveExpression(valueField.Width),
            new CodePrimitiveExpression(valueField.Signed ? true : false)
          )
        )
      );
    }
  }
}
